Fix #80, support an include mechanism for PDI config#653
Fix #80, support an include mechanism for PDI config#653JAuriac wants to merge 1 commit intopdidev:mainfrom
Conversation
| PC_tree_t pdi = PC_get(conf, ".pdi"); | ||
| if (!PC_status(pdi)) { | ||
| root = pdi; | ||
| } |
There was a problem hiding this comment.
please don't do that ! It's up to the user to do that if they want.
Maybe the syntax could support 2 variants:
either:
include:
- /path/to/my/file.yamlor
include:
- file: /path/to/my/file.yaml
node: .pdi[5]where node is any valid ypath (paraconf) expression
|
As we currently need the full path to read a yaml subfile, the test_07 works on the github runner for linux (through a workaround overwriting a placeholder field in the root yaml file) but not for macos, and not for local |
|
The integrations tests "test_duplicate_yaml_include" and "test_circular_yaml_include" are disabled by default, as they expect failure. We use instead the unitary tests "check_duplicate" and "load_pdi_config_duplicate_data_via_inclusion", using System_error. |
| #ifdef PARACONF_VERSION_MAJOR | ||
| #if (PARACONF_VERSION_MAJOR > 1) || (PARACONF_VERSION_MAJOR == 1 && PARACONF_VERSION_MINOR >= 1) | ||
| #define PDI_HAS_PC_PATH 1 | ||
| #else | ||
| #define PDI_HAS_PC_PATH 0 | ||
| #endif | ||
| #else | ||
| #define PDI_HAS_PC_PATH 0 | ||
| #endif | ||
|
|
There was a problem hiding this comment.
is this really necessary?
There was a problem hiding this comment.
Yes, to use absolute paths with Paraconf < 1.1, and both absolute and relative paths with Paraconf 1.1+
There was a problem hiding this comment.
I'd recommend this pattern:
| #ifdef PARACONF_VERSION_MAJOR | |
| #if (PARACONF_VERSION_MAJOR > 1) || (PARACONF_VERSION_MAJOR == 1 && PARACONF_VERSION_MINOR >= 1) | |
| #define PDI_HAS_PC_PATH 1 | |
| #else | |
| #define PDI_HAS_PC_PATH 0 | |
| #endif | |
| #else | |
| #define PDI_HAS_PC_PATH 0 | |
| #endif | |
| #if defined(PARACONF_VERSION) && (PARACONF_UNTYPED_VERSION >= PARACONF_UNTYPED_COMPUTE_VERSION(1, 1, 0)) | |
| #define PDI_HAS_PC_PATH 1 | |
| #else | |
| #define PDI_HAS_PC_PATH 0 | |
| #endif | |
of you could directly do the test where needed
| namespace { | ||
|
|
||
| void load_data(Context& ctx, PC_tree_t node, bool is_metadata) | ||
| void load_data(Global_context& ctx, PC_tree_t node, bool is_metadata) |
There was a problem hiding this comment.
why changing to global_context?
There was a problem hiding this comment.
Changed again, to use make_and_check_descriptor() which is accessible only from Global_context (and not Context)
| std::unordered_set<std::string> visited; | ||
| collect_plugins_impl(conf, visited); |
There was a problem hiding this comment.
This was used to handle the dual pass. We are now back to a single pass on the PC tree, with two sub-pass to read plugins, then types/metadata/data.
| #ifdef PARACONF_VERSION_MAJOR | ||
| #if (PARACONF_VERSION_MAJOR > 1) || (PARACONF_VERSION_MAJOR == 1 && PARACONF_VERSION_MINOR >= 1) | ||
| #define PDI_HAS_PC_PATH 1 | ||
| #else | ||
| #define PDI_HAS_PC_PATH 0 | ||
| #endif | ||
| #else | ||
| #define PDI_HAS_PC_PATH 0 | ||
| #endif | ||
|
|
There was a problem hiding this comment.
I'd recommend this pattern:
| #ifdef PARACONF_VERSION_MAJOR | |
| #if (PARACONF_VERSION_MAJOR > 1) || (PARACONF_VERSION_MAJOR == 1 && PARACONF_VERSION_MINOR >= 1) | |
| #define PDI_HAS_PC_PATH 1 | |
| #else | |
| #define PDI_HAS_PC_PATH 0 | |
| #endif | |
| #else | |
| #define PDI_HAS_PC_PATH 0 | |
| #endif | |
| #if defined(PARACONF_VERSION) && (PARACONF_UNTYPED_VERSION >= PARACONF_UNTYPED_COMPUTE_VERSION(1, 1, 0)) | |
| #define PDI_HAS_PC_PATH 1 | |
| #else | |
| #define PDI_HAS_PC_PATH 0 | |
| #endif | |
of you could directly do the test where needed
|
|
||
| Global_context::Global_context(PC_tree_t conf) | ||
| : m_logger{"PDI", PC_get(conf, ".logging")} | ||
| , m_plugins{*this, conf} |
There was a problem hiding this comment.
this will load the plugins at the construction of the object and should be reworked
| load_pdi_config(conf); // single tree traversal + two flat sub-passes | ||
|
|
||
| // evaluate pattern after loading plugins | ||
| m_logger.evaluate_pattern(*this); |
There was a problem hiding this comment.
shouldn't this be par of load_pdi_config?
There was a problem hiding this comment.
I kept this separated as it previously was, to not make a unique monolithic function inside of the Global_context definition
| void Global_context::check_duplicate(const PC_tree_t& node, const std::string& name) | ||
| { | ||
| auto [it, inserted] = m_defined.emplace(name, node); | ||
| if (!inserted) { | ||
| // it->second = first definition site (deepest file in include tree, post-order) | ||
| // node = redefinition site (current file being processed) | ||
| #if PDI_HAS_PC_PATH | ||
| const char* orig_path = PC_path(it->second); | ||
| const char* redef_path = PC_path(node); | ||
|
|
||
| bool orig_has_path = orig_path && fs::exists(orig_path); | ||
| bool redef_has_path = redef_path && fs::exists(redef_path); | ||
|
|
||
| if (redef_has_path && orig_has_path) { | ||
| throw Config_error(node, "Duplicate definition of '{}', originally defined in '{}', defined again in '{}'", name, redef_path, orig_path); | ||
| } else if (redef_has_path) { | ||
| throw Config_error(node, "Duplicate definition of '{}', originally defined in string config, defined again in '{}'", name, redef_path); | ||
| } else if (orig_has_path) { | ||
| throw Config_error(node, "Duplicate definition of '{}', originally defined in '{}', defined again in a string config", name, orig_path); | ||
| } | ||
| #endif | ||
| throw Config_error(node, "Duplicate definition of '{}'", name); | ||
| } | ||
| } |
There was a problem hiding this comment.
Don't think you need a dedicated function
There was a problem hiding this comment.
Can we get the 'Config_error' with the lines numbers without a new function ?
| void collect_ordered_nodes_impl( | ||
| PC_tree_t conf, | ||
| std::unordered_set<std::string>& globally_loaded, | ||
| std::unordered_set<std::string>& include_chain, | ||
| std::vector<std::pair<std::string, PC_tree_t>>& ordered_nodes, | ||
| std::size_t& no_path_counter, | ||
| const std::string& known_path = "", | ||
| PC_tree_t include_directive = {}, | ||
| const std::string& parent_id = "" | ||
| ); |
There was a problem hiding this comment.
cound't this be a static function in the cxx?
There was a problem hiding this comment.
'collect_ordered_nodes_impl', yes, but not 'collect_ordered_nodes', as we need a public member, used by 'load_pdi_config' ?
There was a problem hiding this comment.
Moved both collect_ordered_nodes() and collect_ordered_nodes() to the local namespace instead
| /// Traverses the include tree once in post-order (deepest includes first), | ||
| /// filling `ordered_nodes` with (canonical_id, PC_tree_t) pairs. | ||
| /// Diamond/circular detection is handled here. | ||
| void collect_ordered_nodes( | ||
| PC_tree_t conf, | ||
| std::unordered_set<std::string>& globally_loaded, | ||
| std::unordered_set<std::string>& include_chain, | ||
| std::vector<std::pair<std::string, PC_tree_t>>& ordered_nodes, | ||
| const std::string& known_path = "", | ||
| PC_tree_t include_directive = {}, | ||
| const std::string& parent_id = "" | ||
| ); |
There was a problem hiding this comment.
cound't this be a static function in the cxx?
There was a problem hiding this comment.
'collect_ordered_nodes_impl', yes, but not 'collect_ordered_nodes', as we need a public member, used by 'load_pdi_config' ?
There was a problem hiding this comment.
Moved both collect_ordered_nodes() and collect_ordered_nodes() to the local namespace instead
97ff407 to
a9e7c87
Compare
41fed24 to
b9ebda1
Compare
Co-authored-by: Julian Auriac <julian.auriac@cea.fr>
2078b40 to
118b45a
Compare
Support an include mechanism for PDI config, to include other YAML files into root YAML document.
Depends on Paraconf handling of relative path to point to included YAML files.
List of things to check before making a PR
Before merging your code, please check the following:
.clang-format;Fix #issuekeyword to autoclose the issue when merged.